﻿namespace Celeriq.RepositoryAPI.Test
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.ServiceModel;
	using Celeriq.Common;

	[Serializable]
	public partial class ListingItem
	{
		public ListingItem()
		{
			_dimensionList = new List<DimensionItem>();
		}

		internal ListingItem(Celeriq.Common.DataItem item, ListingResults results)
			: this()
		{
			this.ID = (int)item.ItemArray[0];
			this.Make = (string)item.ItemArray[1];
			this.Model = (string)item.ItemArray[2];

			RefinementItem r;
		}

		public int ID { get; set; }
		public string Make { get; set; }
		public string Model { get; set; }
		internal List<DimensionItem> _dimensionList = new List<DimensionItem>();
		public IEnumerable<DimensionItem> DimensionList
		{
			get { return _dimensionList; }
		}

		private static long FloorToNearest(long d, long n)
		{
			if (n <= 0) throw new Exception("The rounding operation cannot be performed.");
			return d - (d % n);
		}

		public Celeriq.Common.DataItem ToTransfer()
		{
			var retval = new Celeriq.Common.DataItem();
			retval.ItemArray = new object[] {
                this.ID,
                this.Make,
                this.Model,
            };
			return retval;
		}
	}

	[Serializable]
	public partial class ListingQuery : Celeriq.Common.BaseListingQuery, System.ICloneable
	{
		partial void LoadFromUrlComplete(string originalUrl);

		public ListingQuery()
			: base()
		{
		}

		internal ListingQuery(Celeriq.Common.DataQuery query)
			: this()
		{
			this.DimensionValueList.AddRange(query.DimensionValueList);
			this.PageOffset = query.PageOffset;
			this.RecordsPerPage = query.RecordsPerPage;
			this.Keyword = query.Keyword;
			this.PageName = query.PageName;
			this.NonParsedFieldList = new NamedItemList();
			if (query.NonParsedFieldList != null)
				this.NonParsedFieldList.AddRange(query.NonParsedFieldList);

			#region Field Filters
			if (query.FieldFilters != null)
			{
				foreach (var ff in query.FieldFilters)
				{
					switch (ff.Name)
					{
						case "ID":
							this.FieldFilters.Add(new FieldFilterID { ID = (int?)ff.Value, Comparer = ff.Comparer });
							break;
						case "Make":
							this.FieldFilters.Add(new FieldFilterMake { Make = (string)ff.Value, Comparer = ff.Comparer });
							break;
						case "Model":
							this.FieldFilters.Add(new FieldFilterModel { Model = (string)ff.Value, Comparer = ff.Comparer });
							break;
						default:
							throw new Exception("Unknown filter!");
					}
				}
			}
			#endregion

			#region Field Sorts
			if (query.FieldSorts != null)
			{
				foreach (var fs in query.FieldSorts)
				{
					switch (fs.Name)
					{
						case "ID":
							this.FieldSorts.Add(new FieldSortID { Name = fs.Name, SortDirection = fs.SortDirection });
							break;
						case "Make":
							this.FieldSorts.Add(new FieldSortMake { Name = fs.Name, SortDirection = fs.SortDirection });
							break;
						case "Model":
							this.FieldSorts.Add(new FieldSortModel { Name = fs.Name, SortDirection = fs.SortDirection });
							break;
						default:
							throw new Exception("Unknown sort!");
					}
				}
			}
			#endregion

		}

		public ListingQuery(string url)
			: this()
		{
			if (string.IsNullOrEmpty(url)) return;
			if (url.Contains("%")) url = System.Web.HttpUtility.UrlDecode(url);
			var originalUrl = url;

			var pageBreak = url.IndexOf('?');
			if (pageBreak != -1 && pageBreak < url.Length - 1)
			{
				this.PageName = url.Substring(0, pageBreak);
				url = url.Substring(pageBreak + 1, url.Length - pageBreak - 1);
			}
			else
			{
				this.PageName = url;
				return;
			}

			#region Parse Query String
			var tuplets = url.Split('&');
			foreach (var gset in tuplets)
			{
				var values = gset.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
				if (values.Length == 2)
				{
					switch (values[0])
					{
						case "d":
							{
								var dValues = values[1].Split(new char[] { '+', ' ' }, StringSplitOptions.RemoveEmptyEntries);
								foreach (var dvidxV in dValues)
								{
									long dvidx;
									if (long.TryParse(dvidxV, out dvidx))
										this.DimensionValueList.Add(dvidx);
								}
							}
							break;
						case "po":
							{
								int po;
								if (int.TryParse(values[1], out po))
									this.PageOffset = po;
							}
							break;
						case "rpp":
							{
								int rpp;
								if (int.TryParse(values[1], out rpp))
									this.RecordsPerPage = rpp;
							}
							break;
						case "ff":
							{
								var filters = values[1].Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
								foreach (var s in filters)
								{
									var svalues = s.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
									if (svalues.Length >= 3)
									{
										Celeriq.Common.ComparisonConstants enumValue;
										if (Enum.TryParse<Celeriq.Common.ComparisonConstants>(svalues[1], out enumValue))
										{
											switch (svalues[0].ToLower())
											{
												case "id":
													int idValue;
													if (int.TryParse(svalues[2], out idValue))
														this.FieldFilters.Add(new FieldFilterID() { Name = "ID", Comparer = enumValue, ID = idValue });
													if (enumValue == Celeriq.Common.ComparisonConstants.Between)
													{
														if (svalues.Length > 3)
														{
															if (int.TryParse(svalues[3], out idValue))
																this.FieldFilters.Last().Value2 = idValue;
															else
																throw new Exception("The URL for field filter 'ID' is invalid!");
														}
														else
															throw new Exception("The URL for field filter 'ID' is invalid!");
													}
													break;
												case "make":
													this.FieldFilters.Add(new FieldFilterMake() { Name = "Make", Comparer = enumValue, Make = svalues[2] });
													if (enumValue == Celeriq.Common.ComparisonConstants.Between)
													{
														if (svalues.Length > 3)
															this.FieldFilters.Last().Value2 = svalues[3];
														else
															throw new Exception("The URL for field filter 'Make' is invalid!");
													}
													break;
												case "model":
													this.FieldFilters.Add(new FieldFilterModel() { Name = "Model", Comparer = enumValue, Model = svalues[2] });
													if (enumValue == Celeriq.Common.ComparisonConstants.Between)
													{
														if (svalues.Length > 3)
															this.FieldFilters.Last().Value2 = svalues[3];
														else
															throw new Exception("The URL for field filter 'Model' is invalid!");
													}
													break;
											}
										}
									}
								}
							}
							break;
						case "fs":
							{
								var sorts = values[1].Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
								foreach (var s in sorts)
								{
									var svalues = s.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
									if (svalues.Length > 0)
									{
										switch (svalues[0].ToLower())
										{
											case "id":
												this.FieldSorts.Add(new FieldSortID() { Name = "ID", SortDirection = (svalues.Length == 1 || svalues[1] != "0" ? Celeriq.Common.SortDirectionConstants.Asc : Celeriq.Common.SortDirectionConstants.Desc) });
												break;
											case "make":
												this.FieldSorts.Add(new FieldSortMake() { Name = "Make", SortDirection = (svalues.Length == 1 || svalues[1] != "0" ? Celeriq.Common.SortDirectionConstants.Asc : Celeriq.Common.SortDirectionConstants.Desc) });
												break;
											case "model":
												this.FieldSorts.Add(new FieldSortModel() { Name = "Model", SortDirection = (svalues.Length == 1 || svalues[1] != "0" ? Celeriq.Common.SortDirectionConstants.Asc : Celeriq.Common.SortDirectionConstants.Desc) });
												break;
										}
									}
								}
							}
							break;
						case "srch":
							this.Keyword = values[1];
							break;
						default:
							if (values.Length >= 2)
							{
								if (this.NonParsedFieldList.Count(x => x.Key == values[0]) > 0)
									this.NonParsedFieldList.First(x => x.Key == values[0]).Value = values[1];
								else
									this.NonParsedFieldList.Add(new NamedItem() { Key = values[0], Value = values[1] });
							}
							break;
					}
				}
			}
			#endregion

			LoadFromUrlComplete(originalUrl);

		}

		public static Celeriq.Common.FieldSort GetSortByName(string name)
		{
			if (name == "ID") return new FieldSortID() { Name = "ID", SortDirection = Celeriq.Common.SortDirectionConstants.Asc };
			if (name == "Make") return new FieldSortMake() { Name = "Make", SortDirection = Celeriq.Common.SortDirectionConstants.Asc };
			if (name == "Model") return new FieldSortModel() { Name = "Model", SortDirection = Celeriq.Common.SortDirectionConstants.Asc };
			return null;
		}

		public Celeriq.Common.DataQuery ToTransfer()
		{
			var retval = new Celeriq.Common.DataQuery();
			retval.DimensionValueList = this.DimensionValueList;
			retval.Credentials = this.Credentials;

			retval.FieldFilters = new List<Celeriq.Common.IFieldFilter>();
			if (this.FieldFilters == null)
				this.FieldFilters = new List<IFieldFilter>();
			foreach (var o in this.FieldFilters)
			{
				if (o is Celeriq.Common.GeoCodeFieldFilter)
				{
					var newFilter = new Celeriq.Common.GeoCodeFieldFilter()
					{
						Latitude = ((Celeriq.Common.GeoCodeFieldFilter)o).Latitude,
						Longitude = ((Celeriq.Common.GeoCodeFieldFilter)o).Longitude,
						Radius = ((Celeriq.Common.GeoCodeFieldFilter)o).Radius,
						Comparer = o.Comparer,
						Name = o.Name,
					};
					((Celeriq.Common.IFieldFilter)newFilter).Value = ((Celeriq.Common.IFieldFilter)o).Value;
					((Celeriq.Common.IFieldFilter)newFilter).Value2 = ((Celeriq.Common.IFieldFilter)o).Value2;
					retval.FieldFilters.Add(newFilter);
				}
				else
				{
					var newFilter = new Celeriq.Common.FieldFilter()
					{
						Comparer = o.Comparer,
						Name = o.Name,
					};
					((Celeriq.Common.IFieldFilter)newFilter).Value = ((Celeriq.Common.IFieldFilter)o).Value;
					((Celeriq.Common.IFieldFilter)newFilter).Value2 = ((Celeriq.Common.IFieldFilter)o).Value2;
					retval.FieldFilters.Add(newFilter);
				}
			}

			retval.FieldSorts = new List<Celeriq.Common.IFieldSort>();
			if (this.FieldSorts == null)
				this.FieldSorts = new List<IFieldSort>();
			foreach (var o in this.FieldSorts)
			{
				retval.FieldSorts.Add(new Celeriq.Common.FieldSort()
				{
					Name = o.Name,
					SortDirection = o.SortDirection,
				});
			}

			if (this.NonParsedFieldList != null)
			{
				this.NonParsedFieldList.ForEach(x => retval.NonParsedFieldList.Add(x));
			}

			retval.Keyword = this.Keyword;
			retval.PageOffset = this.PageOffset;
			retval.RecordsPerPage = this.RecordsPerPage;
			return retval;
		}

		public override string ToString()
		{
			var retval = string.Empty;

			#region Dimensions
			if (this.DimensionValueList != null && this.DimensionValueList.Count > 0)
			{
				retval += "d=" + string.Join("+", this.DimensionValueList.Select(x => x.ToString()));
			}
			#endregion

			#region Field Filters
			if (this.FieldFilters != null && this.FieldFilters.Count > 0)
			{
				var ffURL = string.Empty;
				foreach (var ff in this.FieldFilters)
				{
					var fieldName = string.Empty;
					if (1 == 0) ;
					else if (ff is FieldFilterID) fieldName = "ID";
					else if (ff is FieldFilterMake) fieldName = "Make";
					else if (ff is FieldFilterModel) fieldName = "Model";
					else throw new Exception("The filter type is not valid!");

					var f1 = (Celeriq.Common.IFieldFilter)ff;
					if (ff is Celeriq.Common.GeoCodeFieldFilter)
					{
						var gff = ff as Celeriq.Common.GeoCodeFieldFilter;
						ffURL += fieldName + "," + ff.Comparer.ToString() + "," + gff.Latitude.ToString() + "," + gff.Longitude.ToString() + "," + gff.Radius.ToString() + "|";
					}
					else
					{
						if (ff.Comparer == Celeriq.Common.ComparisonConstants.Between)
						{
							if ((f1.Value != null) && (f1.Value2 != null))
							{
								ffURL += fieldName + "," + ff.Comparer.ToString() + "," + f1.Value.ToString() + "," + f1.Value2.ToString() + "|";
							}
						}
						else
						{
							if (f1.Value != null)
							{
								ffURL += fieldName + "," + ff.Comparer.ToString() + "," + f1.Value.ToString() + "|";
							}
						}
					}
				}

				ffURL = ffURL.Trim('|');
				if (!string.IsNullOrEmpty(ffURL))
					retval += "&ff=" + ffURL;
			}
			#endregion

			#region Field Sorts
			if (this.FieldSorts != null && this.FieldSorts.Count > 0)
			{
				var fsURL = string.Empty;
				foreach (var fs in this.FieldSorts)
				{
					if (1 == 0) ;
					else if (fs is FieldSortID) fsURL += "ID";
					else if (fs is FieldSortMake) fsURL += "Make";
					else if (fs is FieldSortModel) fsURL += "Model";
					else throw new Exception("The sort type is not valid!");
					fsURL += (fs.SortDirection == Celeriq.Common.SortDirectionConstants.Asc ? string.Empty : ",0");
				}

				fsURL = fsURL.Trim('|');
				if (!string.IsNullOrEmpty(fsURL))
					retval += "&fs=" + fsURL;
			}
			#endregion

			#region Keyword
			if (!string.IsNullOrEmpty(this.Keyword))
			{
				retval += "&srch=" + this.Keyword;
			}
			#endregion

			if (this.PageOffset != 1)
				retval += "&po=" + this.PageOffset;
			if (this.RecordsPerPage != 10)
				retval += "&rpp=" + this.RecordsPerPage;

			#region NonParsed Field
			if (this.NonParsedFieldList != null)
			{
				foreach (var item in this.NonParsedFieldList.Where(x => !string.IsNullOrEmpty(x.Value)))
				{
					retval += "&" + item.Key + "=" + item.Value;
				}
			}
			#endregion

			retval = retval.Trim('&');

			if (string.IsNullOrEmpty(this.PageName))
				return retval;
			else if (!string.IsNullOrEmpty(retval))
				return this.PageName + "?" + retval;
			else
				return this.PageName;
		}

		partial void Cloning(ListingQuery query);

		object ICloneable.Clone()
		{
			return this.Clone();
		}

		public ListingQuery Clone()
		{
			var retval = new ListingQuery();
			retval.DimensionValueList.AddRange(this.DimensionValueList);
			retval.FieldFilters.AddRange(this.FieldFilters);
			retval.FieldSorts.AddRange(this.FieldSorts);
			retval.Keyword = this.Keyword;
			retval.NonParsedFieldList.AddRange(this.NonParsedFieldList);
			retval.PageOffset = this.PageOffset;
			retval.PageName = this.PageName;
			retval.RecordsPerPage = this.RecordsPerPage;
			this.Cloning(retval);
			return retval;
		}

	}

	#region Filtering

	[Serializable]
	public sealed class FieldFilterID : Celeriq.Common.FieldFilter, Celeriq.Common.IFieldFilter
	{
		public FieldFilterID() : base("ID") { }
		public int? ID { get; set; }
		object Celeriq.Common.IFieldFilter.Value
		{
			get { return this.ID; }
			set { this.ID = (int?)value; }
		}
	}

	[Serializable]
	public sealed class FieldFilterMake : Celeriq.Common.FieldFilter, Celeriq.Common.IFieldFilter
	{
		public FieldFilterMake() : base("Make") { }
		public string Make { get; set; }
		object Celeriq.Common.IFieldFilter.Value
		{
			get { return this.Make; }
			set { this.Make = (string)value; }
		}
	}

	[Serializable]
	public sealed class FieldFilterModel : Celeriq.Common.FieldFilter, Celeriq.Common.IFieldFilter
	{
		public FieldFilterModel() : base("Model") { }
		public string Model { get; set; }
		object Celeriq.Common.IFieldFilter.Value
		{
			get { return this.Model; }
			set { this.Model = (string)value; }
		}
	}

	#endregion

	#region Sorting

	[Serializable]
	public sealed class FieldSortID : Celeriq.Common.FieldSort, Celeriq.Common.IFieldSort
	{
		public FieldSortID() : base("ID") { }
	}

	[Serializable]
	public sealed class FieldSortMake : Celeriq.Common.FieldSort, Celeriq.Common.IFieldSort
	{
		public FieldSortMake() : base("Make") { }
	}

	[Serializable]
	public sealed class FieldSortModel : Celeriq.Common.FieldSort, Celeriq.Common.IFieldSort
	{
		public FieldSortModel() : base("Model") { }
	}

	#endregion

	[Serializable]
	public partial class ListingResults
	{
		private const int VERSION_HASH = 1076860707;

		public ListingResults()
		{
			this.Query = new ListingQuery();
			this.DimensionList = new List<Celeriq.Common.DimensionItem>();
			this.AllDimensions = new DimensionStore(this);
			this.RecordList = new List<ListingItem>();
		}

		internal ListingResults(Celeriq.Common.DataQueryResults results)
			: this()
		{
			if (results.VersionHash != VERSION_HASH)
			{
				throw new Exception("The generated API is out of date. The repository model has been changed. Please re-generate this code file.");
			}

			this.Query = new ListingQuery(results.Query);
			this.DimensionList = new List<Celeriq.Common.DimensionItem>(results.DimensionList);
			this.AllDimensions = new DimensionStore(this);
			this.TotalRecordCount = results.TotalRecordCount;
			if (results.Query.RecordsPerPage > 0)
				this.TotalPageCount = (results.TotalRecordCount / results.Query.RecordsPerPage) + (results.TotalRecordCount % results.Query.RecordsPerPage == 0 ? 0 : 1);
			this.ComputeTime = results.ComputeTime;

			if (results.RecordList != null)
			{
				foreach (var record in results.RecordList)
				{
					var item = new ListingItem(record, this);
					this.RecordList.Add(item);
				}
				this.DimensionList.RemoveAll(x => x.RefinementList.Count <= 1);
			}
		}

		public List<Celeriq.Common.DimensionItem> DimensionList { get; private set; }
		public ListingQuery Query { get; private set; }
		public List<ListingItem> RecordList { get; private set; }
		public int TotalRecordCount { get; private set; }
		public int TotalPageCount { get; private set; }
		public DimensionStore AllDimensions { get; private set; }
		public long ComputeTime { get; private set; }

	}

	[Serializable]
	public partial class DimensionStore
	{
		private ListingResults _results = null;
		public DimensionStore(ListingResults results)
		{
			_results = results;
		}

	}

	[Serializable]
	public partial class RepositoryConnection
	{
		static RepositoryConnection()
		{
			//This is the default WCF key found in the configuration file.
			//You may set this in the calling code to change this value
			WCFKey = "88a45ec9-c2f8-45d0-9ee2-ee8cce51647d";
			ServerName = "localhost";
		}

		public static string WCFKey { get; set; }
		public static string ServerName { get; set; }

		public static System.ServiceModel.ChannelFactory<Celeriq.Common.IDataModel> GetFactory()
		{
			return GetFactory(RepositoryConnection.ServerName, RepositoryConnection.WCFKey);
		}

		public static System.ServiceModel.ChannelFactory<Celeriq.Common.IDataModel> GetFactory(string serverName, string key)
		{
			var myBinding = new NetTcpBinding();
			myBinding.Security.Mode = SecurityMode.Transport;
			myBinding.MaxBufferPoolSize = 2147483647;
			myBinding.MaxBufferSize = 2147483647;
			myBinding.MaxConnections = 10;
			myBinding.MaxReceivedMessageSize = 2147483647;
			myBinding.ReaderQuotas.MaxDepth = 2147483647;
			myBinding.ReaderQuotas.MaxStringContentLength = 2147483647;
			myBinding.ReaderQuotas.MaxArrayLength = 2147483647;
			myBinding.ReaderQuotas.MaxBytesPerRead = 2147483647;
			myBinding.ReaderQuotas.MaxNameTableCharCount = 2147483647;
			var endpoint = new EndpointAddress("net.tcp://" + serverName + ":1973/" + key);
			return new System.ServiceModel.ChannelFactory<Celeriq.Common.IDataModel>(myBinding, endpoint);
		}

		public static void Clear(Celeriq.Common.IDataModel service, UserCredentials credentials)
		{
			var errorList = service.Clear(credentials);
			if (errorList != null && errorList.Length > 0)
			{
				throw new Exception(errorList.First());
			}
		}

		public static void UpdateData(ListingItem item, UserCredentials credentials, Celeriq.Common.IDataModel service)
		{
			UpdateData(new List<ListingItem>() { item }, credentials, service);
		}

		public static void UpdateData(IEnumerable<ListingItem> list, UserCredentials credentials, Celeriq.Common.IDataModel service)
		{
			var l = new List<Celeriq.Common.DataItem>();
			list.ToList().ForEach(x => l.Add(x.ToTransfer()));
			var errorList = service.UpdateData(l, credentials);
			if (errorList != null && errorList.Length > 0)
			{
				throw new Exception(errorList.First());
			}
		}

		public static void DeleteData(ListingItem item, UserCredentials credentials, Celeriq.Common.IDataModel service)
		{
			DeleteData(new List<ListingItem>() { item }, credentials, service);
		}

		public static void DeleteData(IEnumerable<ListingItem> list, UserCredentials credentials, Celeriq.Common.IDataModel service)
		{
			var l = new List<Celeriq.Common.DataItem>();
			list.ToList().ForEach(x => l.Add(x.ToTransfer()));
			var errorList = service.DeleteData(l, credentials);
			if (errorList != null && errorList.Length > 0)
			{
				throw new Exception(errorList.First());
			}
		}

		public static ListingResults QueryData(ListingQuery query, Celeriq.Common.IDataModel service)
		{
			var t = service.Query(query.ToTransfer());
			if (t.ErrorList != null && t.ErrorList.Length > 0)
			{
				throw new Exception(t.ErrorList.First());
			}
			return new ListingResults(t);
		}

	}

}
